import os
import json
import shutil
import time
import subprocess
import requests
import hashlib
from urllib.parse import parse_qs, urlparse
from libs.aliyun import *
from libs.tencent import *
from libs.email import *
from libs.helper import *

"""

"""


class Task:
    apiUrl = None
    config = None
    ssl_dir = None
    site = None
    logs = None
    certInfo = None

    def job(self, config, ssl_dir):
        self.apiUrl = "https://ssl.dev.jincheng163.com/"
        self.config = config
        self.ssl_dir = ssl_dir

        # 日志记录数组
        self.logs = []

        # 检查认证
        print('检查站点配置...')
        self.log('检查站点配置...')

        # JSON文件路径
        json_path = os.path.join(os.getcwd(), "sites.json")
        if os.path.exists(json_path):
            with open(json_path, 'r') as file:
                data = json.load(file)

            if len(data) == 0:
                print(f'站点配置为0,请配置...{json_path}')
                self.log(f'站点配置为0,请配置...{json_path}')

            updateDomain = []
            for i in range(len(data)):
                self.site = data[i]

                print(
                    f"检查idx-{i}:{self.site['Section']}，SN:{self.site['Sn']}...")
                self.log(
                    f"检查idx-{i}:{self.site['Section']}，SN:{self.site['Sn']}...")

                certEndTimeStr = self.get_certInfo()

                if certEndTimeStr == False:
                    print(f"检查失败，请查看app.log")
                    self.log(f"检查失败，请查看app.log")
                    continue

                # 站点EndTime为空或NULL或与远程时间不同时开启更新
                if not self.site['EndTime'] and certEndTimeStr != self.site['EndTime']:
                    # 下载到本地，并解压
                    self.log('存在差异，下载文件...')
                    res = self.download()
                    if res == False:
                        print(f"下载失败，请查看app.log")
                        self.log(f"下载失败，请查看app.log")
                        continue

                    # 根据不同的节点类型处理
                    if self.site['Section'] == 'local':
                        res = self.local_event()
                    elif self.site['Section'] == 'aliyun':
                        res = self.aliyun_event()
                    elif self.site['Section'] == 'tencent':
                        res = self.tencent_event()
                    else:
                        print(f"无法处理Section：{self.site['Section']}")

                    # 写入更新时间
                    updateState = '失败'
                    if res == True:
                        updateState = '成功'
                        data[i]['EndTime'] = certEndTimeStr
                        with open(json_path, "w") as json_file:
                            json.dump(data, json_file)

                    updateDomain.append(
                        f"[{self.site['Section']}]SN:{self.site['Sn']}，状态：{updateState}")
                else:
                    print('无差异')
                    self.log('无差异')
            # 执行完毕，根据Email发送等级，决定发送内容
            print('本次任务全部完成。')
            self.log('本次任务全部完成。')
            print(self.logs)

            if self.config['main']['sendMail'] == "ON" and self.checkSeticonParams('email') == True:
                send_email(self.config['main'], self.config['email'], {
                    "domain": updateDomain, "logs": self.logs})
        else:
            self.initSite(json_path)
            print('错误 sites.json没找到，初始化已完成，请配置。')
            self.log('错误 sites.json没找到，初始化已完成，请配置。')

    def local_event(self):
        self.log('本地部署...')
        # 复制文件
        Sn = self.site['Sn']
        Path = self.site['Path'].split(",")
        cert_path = f"{self.ssl_dir}/{Sn}"
        for web_path in Path:
            # 复制文件到每一个web_path
            if os.path.exists(web_path) == False:
                return False

            # 复制文件
            path_list = os.listdir(f"{cert_path}")
            for file in path_list:
                fileType = os.path.splitext(file)[1].lower()
                if fileType == '.key':
                    self.log(f'复制文件{file}')
                    shutil.copy2(os.path.join(cert_path, file),
                                 os.path.join(web_path, 'site.key'))
                if file == 'fullchain.cer':
                    self.log(f'复制文件{file}')
                    shutil.copy2(os.path.join(cert_path, file),
                                 os.path.join(web_path, 'site.crt'))

        # 执行Reload
        if self.site['ReloadCommand'] is not None and self.site['ReloadCommand'] != "":
            self.log('重载服务...')
            cmd = subprocess.run(
                self.site['ReloadCommand'], shell=True, stdout=subprocess.PIPE)
            print(cmd.stdout.decode())
            self.log(cmd.stdout.decode())

        print('local部署完成.')
        self.log('local部署完成.')
        return True

    def aliyun_event(self):

        self.log('aliyun CDN 部署...')
        Sn = self.site['Sn']
        cert_path = f"{self.ssl_dir}/{Sn}"

        with open(os.path.join(cert_path, f"{self.certInfo['domain'][0]}.key"), "r") as file:
            sslKey = file.read()
        with open(os.path.join(cert_path, "fullchain.cer"), "r") as file:
            sslPem = file.read()

        # 通过Post提交到CDN域名
        aliyun = Aliyun({'AccessKeyId': self.config['aliyun']['AccessKeyId'],
                        'AccessKeySecret': self.config['aliyun']['AccessKeySecret']})

        # 获得CDN下域名sslUploadCdn
        domain = self.certInfo['domain'][0]
        res = aliyun.cdnDomains({"domain": getMainDomain(domain)})

        if res['code'] != 200:
            # 中止任务，写 message
            print(f"错误，{res['message']} 排除后重试。")
            self.log(f"错误，{res['message']} 排除后重试。")
        else:
            # 取得符合条件的域名
            uploadDomains = getCdnDomains(
                self.certInfo['domain'], res['message'])

            print('符合条件域名', uploadDomains)
            dlist = ''.join(str(i) for i in uploadDomains)
            self.log(f"符合条件域名 {dlist}")

            if len(uploadDomains) == 0:
                # 没有符合条件的域名
                print(f"警告：没有符合部署条件的CDN域名，排除后可重试。")
                self.log(f"警告：没有符合部署条件的CDN域名，排除后可重试。")
            else:
                Paths = self.site['Path'].split(",")  # 仅部署指定域名
                Paths = list(filter(lambda x: x.strip() != "", Paths))
                # 这里一般不会出错，所以不try
                for domain in uploadDomains:
                    if len(Paths) == 0 or (len(Paths) > 0 and domain in Paths):
                        print(f'部署 {domain}')
                        self.log(f'部署 {domain}')
                        aliyun.sslUploadCdn({
                            "domain": domain, "sslKey": sslKey, "sslPem": sslPem})
        # 部署完成
        print(f"aliyun 部署任务完成。")
        self.log(f"aliyun 部署任务完成。")
        return True

    def tencent_event(self):
        self.log('tencent CDN 部署...')
        Sn = self.site['Sn']
        cert_path = f"{self.ssl_dir}/{Sn}"

        with open(os.path.join(cert_path, f"{self.certInfo['domain'][0]}.key"), "r") as file:
            sslKey = file.read()
        with open(os.path.join(cert_path, "fullchain.cer"), "r") as file:
            sslPem = file.read()

        # 通过Post提交到CDN域名
        tencent = Tencent({'AccessKeyId': self.config['tencent']['SecretId'],
                          'AccessKeySecret': self.config['tencent']['SecretKey']})

        # 获得CDN下域名sslUploadCdn
        domain = self.certInfo['domain'][0]
        res = tencent.cdnDomains({"domain": getMainDomain(domain)})

        if res['code'] != 200:
            # 中止任务，写 message
            print(f"错误，{res['message']} 排除后重试。")
            self.log(f"错误，{res['message']} 排除后重试。")
        else:
            # 取得符合条件的域名
            uploadDomains = getCdnDomains(
                self.certInfo['domain'], res['message'])

            print('符合条件域名', uploadDomains)
            dlist = ''.join(str(i) for i in uploadDomains)
            self.log(f"符合条件域名 {dlist}")

            if len(uploadDomains) == 0:
                # 没有符合条件的域名
                print(f"警告：没有符合部署条件的CDN域名，排除后可重试。")
                self.log(f"警告：没有符合部署条件的CDN域名，排除后可重试。")
            else:
                Paths = self.site['Path'].split(",")  # 仅部署指定域名
                Paths = list(filter(lambda x: x.strip() != "", Paths))
                # 这里一般不会出错，所以不try
                for domain in uploadDomains:
                    if len(Paths) == 0 or (len(Paths) > 0 and domain in Paths):
                        print(f'部署 {domain}')
                        self.log(f'部署 {domain}')
                        tencent.sslUploadCdn({
                            "domain": domain, "sslKey": sslKey, "sslPem": sslPem})
        # 部署完成
        print(f"tencent 部署任务完成。")
        self.log(f"tencent 部署任务完成。")
        return True

    def get_certInfo(self):
        # 检查站点证书过期与否,先检查本地，再检查网络
        now = getTimestamp()
        # 本地标识过期前7天开始检查
        if check_string(self.site['EndTime']) == True and int(self.site['EndTime']) > now+7*86400:
            return int(self.site['EndTime'])

        Sn = self.site['Sn']

        if check_string(Sn) == False:
            print('配置中SN为空。')
            self.log('配置中SN为空。')
            return False

        try:
            url = f"{self.apiUrl}getcertinfo/{Sn}"
            headers = self.getHeaders()
            R = requests.get(url, headers=headers)
            if R.status_code == 200:
                info = json.loads(R.text)
                if info['code'] == 200:
                    self.certInfo = info
                    return info['expired_at']
                else:
                    print(f"查找失败 {info['message']}")
                    return False
            else:
                print(f'检查失败,code:{R.status_code}')
                self.log(f'检查失败,code:{R.status_code}')
                logging.error(R)
                return False
        except Exception as error:
            print('error ..', error)
            logging.error(error)
            return False

    def download(self):
        # 下载文件
        Sn = self.site['Sn']
        try:
            url = f"{self.apiUrl}getssl/{Sn}"
            file_name = f"{self.ssl_dir}/{Sn}.zip"
            self.download_url(url, file_name)
            # 解压
            unzip_file(file_name, f"{self.ssl_dir}/{Sn}")
            return True
        except Exception as error:
            print('error:', error)
            logging.error(error)
            return False

    def download_url(self, url, save_path, chunk_size=128):
        r = requests.get(url, stream=True)
        with open(save_path, 'wb') as fd:
            for chunk in r.iter_content(chunk_size=chunk_size):
                fd.write(chunk)

    def getHeaders(self):
        return {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", 'Content-Type': 'application/json', 'charset': 'UTF-8'}

    def log(self, text):
        Time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        self.logs.append(f"[{Time}]:{text}")

    # 检查节点参数是否为空

    def checkSeticonParams(self, settion):
        for key, value in self.config.items(settion):
            if not value:
                print(f'「{settion}」配置中的{key}必须配置')
                return False
        return True

    def initSite(self):
        json_path = os.path.join(os.getcwd(), "sites.json")
        # 初始化站点JSON
        data = [
            {
                "Section": "",
                "Sn": "",
                "EndTime": "",
                "Path": "",
                "ReloadCommand": ""
            }]

        with open(json_path, "w") as file:
            json.dump(data, file)
